#include			<stdlib.h>
#include			<unistd.h>
#include			<stdio.h>
#include			<string.h>
#include			<sys/ptrace.h>
#include			<sys/user.h>

#include			"list.h"
#include			"heap.h"
#include			"ftrace.h"
#include			"syscall.h"
#include			"getsymaddr_internal.h"
#include			"read_asm.h"

static s_func			*new_func(unsigned long call_addr,
    unsigned long sym_addr, unsigned char is_syscall)
{
  static unsigned long		id = 0;
  s_func			*func;

  if (NULL != (func = malloc(sizeof(*func))))
  {
    func->id = id++;
    func->call_addr = call_addr;
    func->sym_addr = sym_addr;
    func->is_syscall = is_syscall;
    func->name = NULL;
  }
  return (func);
}

static int			check_regs(pid_t pid, unsigned long *last_call_addr,
    s_heap **current, unsigned long next)
{
  s_func			*func;
  struct user_regs_struct	regs;

  if (-1 == ptrace(PT_GETREGS, pid, NULL, &regs))
  {
    perror("ptrace");
    return (-1);
  }
  if (is_call(next))
    *last_call_addr = regs.rip;
  else if (is_rt(next) && *current && (*current)->parent)
    if (NULL == *current)
      return (-1);
    else
      *current = (*current)->parent;
  else if (is_syscall(next))
  {
    if (NULL == (func = new_func(regs.rip, regs.rax, 1)))
      return (-1);
    if (regs.rax < syscall_nbr)
      func->name = syscall_list[regs.rax];
    if (*current)
      heap_add(*current, func);
  }
  return (0);
}

int				getsymaddr(pid_t pid, s_heap **heap,
    s_heap **current, s_list *symlist)
{
  long				next;
  struct user_regs_struct	regs;
  static unsigned long		last_call_addr = 0;
  s_func			*func;

  if (-1 == ptrace(PT_GETREGS, pid, NULL, &regs) ||
      -1 == (next = ptrace(PT_READ_I, pid, (void *) regs.rip, 0)))
    return (-1);
  if (!*current || last_call_addr)
  {
    if (NULL == (func = new_func(last_call_addr, regs.rip, 0)))
      return (-1);
    if (0 == get_symbol(symlist, func))
    {
      if (NULL == (func->name = malloc(512 * sizeof(char))))
	return (-1);
      func->name[511] = 0;
      snprintf(func->name, 511, "0x%lx", func->sym_addr);
    }
    if (NULL == (*current = heap_add(*current, func)))
      return (-1);
    *heap =  (!*heap) ? *current : *heap;
    last_call_addr = 0;
  }
  return (check_regs(pid, &last_call_addr, current, next));
}
